/**
 * 
 */
package org.stars.daostars.core.runtime;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;

/**
 * Questa classe è stata derivata per ottenere i tempi di esecuzione delle query
 * e del fill della lista degli entity bean.
 * 
 * Singleton pattern: http://en.wikipedia.org/wiki/Singleton_pattern
 */
public class DaoQueryRunner extends QueryRunner {
	
	   // Private constructor prevents instantiation from other classes
	   protected DaoQueryRunner() {
	   }
	 
	   /**
	    * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
	    * or the first access to SingletonHolder.INSTANCE, not before.
	    */
	   private static class DaoQueryRunnerHolder { 
	     public static final DaoQueryRunner INSTANCE = new DaoQueryRunner();
	   }
	 
	   public static DaoQueryRunner getInstance() {
	     return DaoQueryRunnerHolder.INSTANCE;
	   }


	/**
	 * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
	 * 
	 * @param conn
	 *            The Connection to use to run the query. The caller is
	 *            responsible for closing this Connection.
	 * @param sql
	 *            The SQL to execute.
	 * @param params
	 *            An array of query replacement parameters. Each row in this
	 *            array is one set of batch replacement values.
	 * @return The number of rows updated per statement.
	 * @throws SQLException
	 *             if a database access error occurs
	 * @since DbUtils 1.1
	 */
	@Override
	public int[] batch(Connection conn, String sql, Object[][] params)
			throws SQLException {

		PreparedStatement stmt = null;
		int[] rows = null;
		try {
			stmt = this.prepareStatement(conn, sql);

			for (int i = 0; i < params.length; i++) {
				this.fillStatement(stmt, params[i]);
				stmt.addBatch();
			}
			rows = stmt.executeBatch();

		} catch (SQLException e) {
			rethrow(e, sql, params);
		} finally {
			close(stmt);
		}

		return rows;
	}

	/**
	 * Execute an SQL SELECT query with replacement parameters. The caller is
	 * responsible for closing the connection.
	 * 
	 * @param conn
	 *            The connection to execute the query in.
	 * @param sql
	 *            The query to execute.
	 * @param params
	 *            The replacement parameters.
	 * @param rsh
	 *            The handler that converts the results into an object.
	 * @return The object returned by the handler.
	 * @throws SQLException
	 *             if a database access error occurs
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Object query(Connection conn, String sql, Object[] params,
			ResultSetHandler rsh) throws SQLException {

		long timeSql;
		long timeList;
		EntityBeanRsHandler ebrh = null;
		if (rsh instanceof EntityBeanRsHandler) {
			ebrh = (EntityBeanRsHandler) rsh;
		}
		PreparedStatement stmt = null;
		ResultSet rs = null;
		Object result = null;

		try {
			timeSql = System.currentTimeMillis();
			stmt = this.prepareStatement(conn, sql);
			this.fillStatement(stmt, params);			
			rs = this.wrap(stmt.executeQuery());
			
			// finita la query inizia il timer x il popolamento			
			// delle liste			
			timeSql = System.currentTimeMillis() - timeSql;			
			timeList = System.currentTimeMillis();
			result = rsh.handle(rs);
			timeList = System.currentTimeMillis() - timeList;

			if (ebrh != null) {
				ebrh.setExecutionTimeOfSql(timeSql);
				ebrh.setExecutionTimeOfRetriveList(timeList);
			}

		} catch (SQLException e) {
			this.rethrow(e, sql, params);

		} finally {
			try {
				close(rs);
			} finally {
				close(stmt);
			}
		}

		return result;
	}

	/**
	 * Execute an SQL INSERT, UPDATE, or DELETE query.
	 * 
	 * @param conn
	 *            The connection to use to run the query.
	 * @param sql
	 *            The SQL to execute.
	 * @param params
	 *            The query replacement parameters.
	 * @return The number of rows updated.
	 * @throws SQLException
	 *             if a database access error occurs
	 */
	@Override
	public int update(Connection conn, String sql, Object[] params)
			throws SQLException {
		
		PreparedStatement stmt = null;
		int rows = 0;

		try {
			if (sql.toLowerCase().trim().startsWith("call"))
			{
				stmt = conn.prepareCall(" { "+sql+" } ");
				this.fillStatement(stmt, params);		
				stmt.execute();
				rows=0;
			} else {
				stmt = this.prepareStatement(conn, sql);
				this.fillStatement(stmt, params);	
				rows = stmt.executeUpdate();
			}
			
		} catch (SQLException e) {
			this.rethrow(e, sql, params);

		} finally {
			close(stmt);
		}

		return rows;
	}			
}
